package me.timlong.service.impl;

import me.timlong.dao.ItemDOMapper;
import me.timlong.dao.ItemStockDOMapper;
import me.timlong.dao.StockLogDOMapper;
import me.timlong.dataobject.ItemDO;
import me.timlong.dataobject.ItemStockDO;
import me.timlong.dataobject.StockLogDO;
import me.timlong.error.BusinessException;
import me.timlong.error.EmBusinessError;
import me.timlong.mq.MqProducer;
import me.timlong.service.ItemService;
import me.timlong.service.PromoService;
import me.timlong.service.model.ItemModel;
import me.timlong.service.model.PromoModel;
import me.timlong.validator.ValidationResult;
import me.timlong.validator.ValidatorImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class ItemServiceImpl implements ItemService {

    @Autowired
    private ValidatorImpl validator;

    @Autowired
    private ItemDOMapper itemDOMapper;

    @Autowired
    private ItemStockDOMapper itemStockDOMapper;

    @Autowired
    private PromoService promoService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private MqProducer mqProducer;

    @Autowired
    private StockLogDOMapper stockLogDOMapper;

    private ItemDO convertItemDOFromItemModel(ItemModel itemModel) {
        if (null == itemModel)
            return null;
        ItemDO itemDO = new ItemDO();
        BeanUtils.copyProperties(itemModel, itemDO);
        // 这里BeanUtils的copy方法只会拷贝类型相同的字段，字段不相同则不会拷贝，所以这里需要特别设置一下从bigdecimal到dobule的转化
        itemDO.setPrice(itemModel.getPrice().doubleValue());
        return itemDO;
    }

    private ItemStockDO convertItemStockDOFromItemModel(ItemModel itemModel) {
        if (null == itemModel)
            return null;

        ItemStockDO itemStockDO = new ItemStockDO();
        itemStockDO.setItemId(itemModel.getId());
        itemStockDO.setStock(itemModel.getStock());
        return itemStockDO;
    }

    @Override
    @Transactional // 事务
    public ItemModel createItem(ItemModel itemModel) throws BusinessException {
        // 创建商品信息的步骤，养成良好的习惯
        // 1. 入参校验
        ValidationResult result = validator.validate(itemModel);
        if (result.isHasErrors()) {
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, result.getErrMsg());
        }

        //2. 转化ItemModel ==> DataObject
        ItemDO itemDO = this.convertItemDOFromItemModel(itemModel);

        //3. 写入数据库
        this.itemDOMapper.insertSelective(itemDO);
        itemModel.setId(itemDO.getId());

        ItemStockDO itemStockDO = this.convertItemStockDOFromItemModel(itemModel);
        this.itemStockDOMapper.insertSelective(itemStockDO);

        //4. 返回==创建完成==的对象, 因此这个创建完成的对象应该是从数据中取出来而非直接返回传入的参数
        ItemModel itemModelForReturn = this.getItemById(itemModel.getId());
        return itemModelForReturn;
    }

    @Override
    /**
     * 列出商品信息
     */
    public List<ItemModel> listItem() {
        List<ItemDO> itemDOS = this.itemDOMapper.listItem();

        // Java8新特性 stream
        List<ItemModel> itemModelList = itemDOS.stream().map(itemDO -> {
            ItemStockDO itemStockDO = itemStockDOMapper.selectByItemId(itemDO.getId());
            if(null == itemStockDO) {
                itemStockDO = new ItemStockDO();
                itemStockDO.setItemId(itemDO.getId());
                itemStockDO.setStock(0);
            }
            ItemModel itemModel = this.convertModelFromDataObject(itemDO, itemStockDO);
            return itemModel;
        }).collect(Collectors.toList());
        return itemModelList;
    }

    @Override
    public ItemModel getItemById(Integer id) {

        ItemDO itemDO = this.itemDOMapper.selectByPrimaryKey(id);
        // 获取库存
        ItemStockDO itemStockDO = this.itemStockDOMapper.selectByItemId(itemDO.getId());
        // dataobject -> model
        ItemModel itemModel = this.convertModelFromDataObject(itemDO, itemStockDO);

        // 获取商品的活动信息
        PromoModel promoModel = promoService.getPromoByItemId(itemModel.getId());
        if (null != promoModel && promoModel.getStatus().intValue() != 3)
            itemModel.setPromoModel(promoModel);


        return itemModel;
    }

    @Override
    public ItemModel getItemByIdInCache(Integer id) {
        ItemModel itemModel = (ItemModel) redisTemplate.opsForValue().get("item_validate_" + id);
        if(null == itemModel) {
            itemModel = this.getItemById(id);
            redisTemplate.opsForValue().set("item_validate_" + id, itemModel);
            redisTemplate.expire("item_validate_" + id, 10, TimeUnit.MINUTES);
        }
        return itemModel;
    }

    @Override
    public boolean decreaseStock(Integer itemId, Integer amount) throws BusinessException {
        // int affectedRow = itemStockDOMapper.decreaseStock(itemId, amount);
        // 更改减少库存的逻辑， 减少内存中的库存缓存信息
        // 库存更新后的结果
        Long result = redisTemplate.opsForValue().increment("promo_item_stock_" + itemId, amount * -1);

        if (result > 0) {
            // 更新库存成功
            // boolean mqResult = mqProducer.asyncReduceStock(itemId, amount);
            // if(!mqResult) {
            //     redisTemplate.opsForValue().increment("promo_item_stock_" + itemId, amount);
            //     return false;
            // }
            return true;
        } else if(result == 0) {
            // 如果库存变为0，则在redis数据库年终打上库存售罄的表示
            redisTemplate.opsForValue().set("promo_item_stock_invalid_" + itemId, "true");
            return true;

        } else {
            // 更新库存失败
            // redisTemplate.opsForValue().increment("promo_item_stock_" + itemId, amount);
            increaseStock(itemId, amount);
            return false;
        }
    }

    @Override
    public boolean increaseStock(Integer itemId, Integer amount) throws BusinessException {
        // 不考虑对应的redis更新失败的情况，这里固定返回一个true
        redisTemplate.opsForValue().increment("promo_item_stock_" + itemId, amount);
        return true;
    }

    @Override
    public boolean increaseSales(Integer itemId, Integer amount) {

        return false;
    }

    @Override
    public boolean asyncDecreaseStock(Integer itemId, Integer amount) {
        return mqProducer.asyncReduceStock(itemId, amount);
    }


    // 初始化库存流水
    @Override
    @Transactional
    public String initStockLog(Integer itemId, Integer amount) {
        StockLogDO stockLogDO = new StockLogDO();
        stockLogDO.setItemId(itemId);
        stockLogDO.setAmount(amount);
        // 1表示初始状态， 2表示下单扣减库存成功， 3表示下单回滚
        stockLogDO.setStockLogId(UUID.randomUUID().toString().replace("-", ""));
        stockLogDO.setStatus(1);

        stockLogDOMapper.insertSelective(stockLogDO);

        return stockLogDO.getStockLogId();
    }

    private ItemModel convertModelFromDataObject(ItemDO itemDO, ItemStockDO itemStockDO) {
        ItemModel itemModel = new ItemModel();
        BeanUtils.copyProperties(itemDO, itemModel);
        itemModel.setPrice(new BigDecimal(itemDO.getPrice()));
        itemModel.setStock(itemStockDO.getStock());

        return itemModel;
    }
}
